function [prm]=bciSSVEPControl(varargin)
% [prm]=bciSSVEPControl(bci,action,prm)
% controls the stimulus and its parameters
% bci.paradigm.id - 11 = SSVEP object selection
% bci.paradigm.id - 13 = SSVEP selection incl. grasping

if length(varargin)==3,
    switch varargin{1}.paradigm.id,
        case 11,
%             prm=bciParadigmID11(bci,action,prm);
            prm=bciParadigmID11(varargin{1},varargin{2},varargin{3});
        case 13,
            prm=bciParadigmID13(varargin{:});
    end
else
    if isempty(varargin),
        objAlignment='x';
    else
        objAlignment = varargin{1};
    end
    if strcmp(objAlignment,'-')||strcmp(objAlignment,'o'),
            prm = bciParadigmID13(struct('paradigm',struct('objectAlignment',objAlignment),...
                                 'numTrials',0),'init',struct('mode',3)); %mode 3 precalculates grasps
    else
            prm = bciParadigmID11(struct('paradigm',struct('objectAlignment',objAlignment),...
                                 'numTrials',0),'init',struct('mode',0));
    end
end


% paradigm dependent functions
%--------------------------------------------------
% SSVEP selection task
%--------------------------------------------------
function prm=bciParadigmID11(bci,action,prm)
if strcmp(action,'select'),
    % stop flicker
    if prm.curEvent ~= 8,
        prm.hitTarget=false;
        prm.mostPred = 0;
        return
    end
    if prm.mode>0 && ~prm.trialClassified && ~isempty(prm.predictedInTrial), % feedback call
        % set ring color according to prediction probability
        probs = sum((bci.eventsToClassify'*ones(1,length(prm.predictedInTrial))-...
                                 (ones(length(bci.eventsToClassify),1)*prm.predictedInTrial))==0,2);
        probs = probs/sum(probs);
        [prm.mostPred evtNo]= max(probs);
        prm.trialClassified = length(prm.predictedInTrial)==prm.nPredToSelection;
        prm.correctTarget = evtNo == prm.lastFlickerEvent;
        objID = prm.objID(prm.positionMat(:,prm.flickerCount)==evtNo);
        setRESI('setRingColor','idx',objID,'red',0.0,'green',0.8,'blue',0.0);
        setRESI('setRingTransparency','idx',objID,'transparency',min(0.5,1-probs(evtNo)));
        notAttended = setdiff(1:prm.nObjects,evtNo);
        for k=notAttended,
            objID = prm.objID(prm.positionMat(:,prm.flickerCount)==k);
            setRESI('setRingColor','idx',objID,'red',0.8,'green',0.0,'blue',0.0);
            setRESI('setRingTransparency','idx',objID,'transparency',min(0.5,1-probs(k)));
        end
        prm.hitSuccess = prm.trialClassified && prm.correctTarget;   % success if selected is intended event
    elseif prm.mode==0 && ~prm.hitTarget && (prm.curEvent>0), % train run call
%         % set ring color according to attended object 
%         objID = bci.paradigm.attendObjectID;
        % set ring color according to attended object randomly (70% prob
        % correct)
        eventPool = [setdiff(prm.objID,bci.paradigm.attendObjectID),...
            repmat(bci.paradigm.attendObjectID,1,7)];
        objID = eventPool(ceil(rand(1)*length(eventPool)));
        setRESI('setRingColor','idx',objID,'red',0.0,'green',0.8,'blue',0.0);
        setRESI('setRingTransparency','idx',objID,'transparency',0);
        notAttended = setdiff(prm.objID,objID);
        for k=notAttended,
            setRESI('setRingColor','idx',k,'red',0.8,'green',0.0,'blue',0.0);
            setRESI('setRingTransparency','idx',k,'transparency',0.5);            
        end
    else
        prm.mostPred=0;
    end
    prm.hitTarget = prm.trialClassified||prm.mode==0; % hit target is true when trial is over
    if prm.hitTarget,
       for k=1:prm.nObjects,
            setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',0,...
                    'maxVisibleFrames',prm.maxVisibleFrames);
       end
    end
elseif strcmp(action,'init'),
    if isfield(bci,'param'),
        prm.nPredToSelection = 1+floor((bci.movDur-bci.param.winsize)*bci.param.winsrate);
    end
    prm.maxVisibleFrames = 2;
    prm.objID=0:3;
    ringThickness = 0.005;

    if strcmp(bci.paradigm.objectAlignment,'+'),
        fixCrossPos=[0,0.0];
        diskSize=0.16;
        prm.objXY=[fixCrossPos(1) -0.33;-0.33,fixCrossPos(2);...
            0.33,fixCrossPos(2); fixCrossPos(1),0.33];
        objScaling=[1,1,1,0.7];
        vp=[0,1.162,0.48,-1,0,0.00,1.3];
        bo = [1,0,0,45;1,0,0,-45;0,0,1,90;-1,0,0,120];
    elseif  strcmp(bci.paradigm.objectAlignment,'x'),
        fixCrossPos=[0,0.05];
        diskSize=0.20;
        prm.objXY=[-0.35 -0.3;-0.33,0.33; 0.35,-0.30; 0.33,0.33];
        objScaling=[1,1,1,0.7];
        vp=[0,1.162,0.48,-1,0,0.00,1.3];
        bo = [1,0,0,45;1,0,0,-45;0,0,1,90;-1,0,0,120];
    elseif  strcmp(bci.paradigm.objectAlignment,'-'), % new virtual robot scenario
        fixCrossPos=[0.28,0.0];
        diskSize=0.18;
        prm.objXY=[0.4 -0.28; 0.55,-0.11; 0.55, 0.11; 0.4,0.28];
        objScaling=[1,1,1,1];
        vp = [0.8,0.0,0.56,0.3,0.3,0.9,1.6666];
        bo = [0,0,0,0;0,0,0,0;0,0,0,0;0,0,0,0];
    else
        diskSize=0.18;
        vp = [0.8,0.0,0.56,0.3,0.3,0.9,1.6666];
    end
    % initialize control parameters
    prm.flickerCount = 0;
    prm.nObjects = length(prm.objID);
    prm.nTrials = prm.nObjects*bci.numTrials;
    % flickerMat defines which object flickers at which frequency
    prm.flickerMat = ones(prm.nObjects,prm.nTrials)*eps;
    % positionMat defines which Object appears at which position
    prm.positionMat = zeros(prm.nObjects,prm.nTrials);

    % set ViewPoint
    setRESI('setViewpoint','x',vp(1),'y',vp(2),'z',vp(3),'rotX',vp(4),...
            'rotY',vp(5),'rotZ',vp(6),'angle',vp(7));
        
    for k=1:prm.nObjects,
        % object scaling
        setRESI('setBodyScale','idx',prm.objID(k),'scale',objScaling(k)); 
        % object orientation
        setRESI('setBodyOrientation','idx',prm.objID(k),'x',bo(k,1),'y',bo(k,2),'z',bo(k,3),'angle',bo(k,4));
        % Object positions
        setRESI('setBodyPosition','idx',prm.objID(k),...
            'x',prm.objXY(k,1),'y',prm.objXY(k,2));
        setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',eps,...
            'maxVisibleFrames',prm.maxVisibleFrames);
        %decouple each object
        setRESI('coupleObjectFrequency','idx',prm.objID(k),'otheridx',-1);
        % disk and ring color
        setRESI('setDiskColor','idx',prm.objID(k),'red',1,'green',1,'blue',1);
        setRESI('setRingColor','idx',prm.objID(k),'red',0.2,'green',0.2,'blue',0.2);
        setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
    end
    setRESI('setBodyPosition','idx',4,'x',fixCrossPos(1),'y',fixCrossPos(2)); % fixation cross

    %disk scaling
    setRESI('setDiskDiameter','diameter',diskSize);
    setRESI('setRingThickness','thickness',ringThickness);
    
    % couple the calibrator at the attended object
    if isfield(bci.paradigm,'attendObjectID'),
        setRESI('coupleCalibratorFrequency','idx',bci.paradigm.attendObjectID);
    end
    prm.trialClassified = false;
    prm.hitTarget = prm.mode==0;
    prm.hitSuccess=false;   
elseif strcmp(action,'newevent')
    if ~any(prm.curEvent==bci.eventsToClassify),
        if prm.curEvent==8, % start flicker event
            for objCount=1:prm.nObjects,
                setRESI('setObjectFrequency','idx',prm.objID(objCount),...
                    'frequency',prm.flickerMat(objCount,prm.flickerCount),...
                    'maxVisibleFrames',prm.maxVisibleFrames);
            end
        else
            % stop flicker
            for k=1:prm.nObjects,
                setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',0,...
                    'maxVisibleFrames',prm.maxVisibleFrames);
                setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
            end            
        end
    else % set position
        % set flicker Counter
        prm.flickerCount = prm.flickerCount + 1;
        % determine where each object appears
        prm.positionMat(prm.objID==bci.paradigm.attendObjectID,prm.flickerCount)=prm.curEvent;
        remainingPos = setdiff(1:prm.nObjects,prm.curEvent);
        prm.positionMat(prm.objID~=bci.paradigm.attendObjectID,prm.flickerCount)=...
            remainingPos(randperm(prm.nObjects-1));
        % determine at which frequency each object flickers
        prm.flickerMat(:,prm.flickerCount) = ...
            bci.paradigm.flickerFreqs(bci.paradigm.flickerAlignment(prm.positionMat(:,prm.flickerCount)));
        % stop flicker
        for k=1:prm.nObjects,
            setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',0,...
                'maxVisibleFrames',prm.maxVisibleFrames);
            setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);            
        end
        % realign the objects
        for objCount=1:prm.nObjects,
            % show flicker frequency at random position
            % remaining Objects flicker at remaining frequencies
            setRESI('setBodyPosition','idx',prm.objID(objCount),...
                'x',prm.objXY(prm.positionMat(objCount,prm.flickerCount),1),...
                'y',prm.objXY(prm.positionMat(objCount,prm.flickerCount),2));
        end        
        prm.lastFlickerEvent = prm.curEvent;
    end
    prm.trialClassified = false;
    prm.hitTarget = false;
    prm.hitSuccess = false;
elseif strcmp(action,'finish')
    % stop flicker
    for k=1:prm.nObjects,
        setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',0,...
            'maxVisibleFrames',prm.maxVisibleFrames);
        setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
    end
end

%--------------------------------------------------
% SSVEP selection task with gripper control
%--------------------------------------------------
function prm=bciParadigmID13(bci,action,prm)
if strcmp(action,'select'),
    if prm.curEvent ~= 8,
        prm.hitTarget=false;
        prm.mostPred = 0;
        if prm.gripperState==4,
            prm=bciExecuteGraspFeedback(prm);
        end
        return
    end
    if prm.mode>0 && ~prm.trialClassified && ~isempty(prm.predictedInTrial), % feedback call
        % set ring color according to prediction probability
        probs = sum((bci.eventsToClassify'*ones(1,length(prm.predictedInTrial))-...
                                 (ones(length(bci.eventsToClassify),1)*prm.predictedInTrial))==0,2);
        probs = probs/sum(probs);
        [prm.mostPred evtNo]= max(probs);
        prm.trialClassified = length(prm.predictedInTrial)==prm.nPredToSelection;
        prm.correctTarget = evtNo == prm.lastFlickerEvent;
        objID = prm.objID(evtNo);
        if bci.paradigm.feedbackType > 1,            
            while ~prm.readyForNextPrediction,
                prm = bciExecuteGraspFeedback(prm);
            end
            prm.readyForNextPrediction = false;
            prm.currentObject = objID;
            prm = bciExecuteGraspFeedback(prm);
            prm.trialComplete = ~true; % need this variable???
            
        else
            setRESI('setRingColor','idx',objID,'red',0.0,'green',0.8,'blue',0.0);
            setRESI('setRingTransparency','idx',objID,'transparency',min(0.5,1-probs(evtNo)));
            notAttended = setdiff(1:prm.nObjects,evtNo);
            for k=notAttended,
                objID = prm.objID(k);
                setRESI('setRingColor','idx',objID,'red',0.8,'green',0.0,'blue',0.0);
                setRESI('setRingTransparency','idx',objID,'transparency',min(0.5,1-probs(k)));
            end
            prm.trialComplete = true;
        end
        prm.hitSuccess = prm.trialClassified && prm.correctTarget;   % success if selected is intended event
    elseif prm.mode==0 && ~prm.hitTarget && (prm.curEvent>0), % train run call
%         % set ring color according to attended object 
%         objID = prm.attendObject;
        % set ring color according to attended object randomly (70% prob
        % correct)
        eventPool = [setdiff(prm.objID,prm.attendObject),...
            repmat(prm.attendObject,1,7)];
        objID = eventPool(ceil(rand(1)*length(eventPool)));
        setRESI('setRingColor','idx',objID,'red',0.0,'green',0.8,'blue',0.0);
        setRESI('setRingTransparency','idx',objID,'transparency',0);
        notAttended = setdiff(prm.objID,objID);
        for k=notAttended,
            setRESI('setRingColor','idx',k,'red',0.8,'green',0.0,'blue',0.0);
            setRESI('setRingTransparency','idx',k,'transparency',0.5);            
        end
    else
        prm.mostPred=0;
    end
    % control gripper
    if prm.gripperState~=0,
        prm=bciExecuteGraspFeedback(prm);
    end
    if prm.gripperState==4,
        prm.trialComplete = prm.trialClassified;
    end
    prm.hitTarget =  prm.trialComplete || prm.mode==0;
    if prm.isFlickering &&( prm.trialClassified||prm.mode==0), % stop flicker
       for k=1:prm.nObjects,
            setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',0,...
                    'maxVisibleFrames',prm.maxVisibleFrames);
       end
    end
elseif strcmp(action,'init'),
    if isfield(bci,'param'),
        prm.nPredToSelection = 1+floor((bci.movDur-bci.param.winsize)*bci.param.winsrate);
    end
    prm.maxVisibleFrames = 2;
    prm.objID=0:3;

    diskSize=0.15; % disk size
    ringThickness = 0.003;

    if true % new virtual robot scenario
        fixCrossPos=[0.28,0.0]; % fixation cross
        prm.objXY=[ 0.55,-0.106; 0.44 -0.28; 0.55, 0.106; 0.44,0.28]; % object position
        objScaling=[1,1,1,1];   % object size
        %vp = [0.8,0.0,0.56,0.3,0.3,0.9,1.6666]; % view point
        vp = [1.24,0.0,0.45,0.45,0.45,0.75,1.85]; % view point
        bo = [0,0,0,0;0,0,0,0;0,0,0,0;0,0,0,0]; % object orientation
    end
    % initialize control parameters
    prm.nObjects = length(prm.objID);
    prm.nTrials = prm.nObjects*bci.numTrials;
    prm.gripperState = 0; % 0=default position, 1=gripping,2=pick object 3=replace object, 4=reset gripper
    prm.trialComplete = false;
    prm.isFlickering = false;
    
    % set ViewPoint
    setRESI('setViewpoint','x',vp(1),'y',vp(2),'z',vp(3),'rotX',vp(4),...
            'rotY',vp(5),'rotZ',vp(6),'angle',vp(7));
        
    for k=1:prm.nObjects,
        % object scaling
        setRESI('setBodyScale','idx',prm.objID(k),'scale',objScaling(k)); 
        % object orientation
        setRESI('setBodyOrientation','idx',prm.objID(k),'x',bo(k,1),'y',bo(k,2),'z',bo(k,3),'angle',bo(k,4));
        % Object positions
        setRESI('setBodyPosition','idx',prm.objID(k),...
            'x',prm.objXY(k,1),'y',prm.objXY(k,2));
        % turn off flicker
        setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',eps,...
            'maxVisibleFrames',prm.maxVisibleFrames);
        %decouple each object
        setRESI('coupleObjectFrequency','idx',prm.objID(k),'otheridx',-1);
        % disk and ring color
        setRESI('setDiskColor','idx',prm.objID(k),'red',1,'green',1,'blue',1);
        setRESI('setRingColor','idx',prm.objID(k),'red',0.2,'green',0.2,'blue',0.2);
        setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
    end
    setRESI('setBodyPosition','idx',4,'x',fixCrossPos(1),'y',fixCrossPos(2)); % fixation cross

    %disk scaling
    setRESI('setDiskDiameter','diameter',diskSize);
    setRESI('setRingThickness','thickness',ringThickness);

    prm.trialClassified = false;
    prm.hitTarget = prm.mode==0;
    prm.hitSuccess=false;
    prm.readyForNextPrediction = true;
    if prm.mode==3, % initialize grasp calculation
        bciExecuteGraspCalculation(prm.objID);
    end
elseif strcmp(action,'newevent')
    if ~any(prm.curEvent==bci.eventsToClassify),
        if prm.curEvent==8, % start flicker event
            setRESI('setRingTransparency','idx',prm.attendObject,'transparency',1);
            for objCount=1:prm.nObjects,
                setRESI('setObjectFrequency','idx',prm.objID(objCount),...
                    'frequency',prm.flickerMat(objCount),...
                    'maxVisibleFrames',prm.maxVisibleFrames);
            end
            prm.isFlickering = true;
        elseif prm.isFlickering
            % stop flicker
            for k=1:prm.nObjects,
                setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',0,...
                    'maxVisibleFrames',prm.maxVisibleFrames);
                setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
            end            
            prm.isFlickering = false;
        end
    else
        % determine attended Object
        prm.attendObject = prm.objID(prm.curEvent);
        % determine at which frequency each object flickers
        prm.flickerMat = bci.paradigm.flickerFreqs(bci.paradigm.flickerAlignment);
        % stop flicker
        if prm.isFlickering,
            for k=1:prm.nObjects,
                setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',0,...
                        'maxVisibleFrames',prm.maxVisibleFrames);
                setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);            
            end
        end
        % show cue for attended object
        if prm.mode==0 || bci.paradigm.feedbackType==0|| bci.paradigm.feedbackType==3,
            setRESI('setRingColor','idx',prm.attendObject,'red',0.0,'green',0.0,'blue',0.0);
            setRESI('setRingTransparency','idx',prm.attendObject,'transparency',0);
        end
        % couple calibrator
        setRESI('coupleCalibratorFrequency','idx',prm.attendObject);
        prm.lastFlickerEvent = prm.curEvent;
    end
    prm.trialClassified = false;
    prm.trialComplete = false;
    prm.hitTarget = false;
    prm.hitSuccess = false;
elseif strcmp(action,'finish')
    % stop flicker
    if prm.isFlickering,
        for k=1:prm.nObjects,
            setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',0,...
                'maxVisibleFrames',prm.maxVisibleFrames);
            setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
        end
    end
end


